<template>
  <div class="markdown-body blog-content module-panel"
       style="padding-left: 40px;padding-right: 40px;">
    <!--博客内容图片预览插件-->
    <viewer style="display: none" :options="viewerOptions" :images="previewImages" @inited="inited" class="viewer"
            ref="viewer">
      <template slot-scope="scope">
        <img v-for="src in previewImages" :src="src" :data-source="src" :key="src">
      </template>
    </viewer>

    <!--博客周边属性信息-->
    <div class="blog-tag-category animated fadeInUp">
      <a key="folder" style="color: gray;">
        <a-icon type="folder"/>
        {{blog.categoryName}}</a>
      <a utton v-for="tag of blog.blogTagList" style="color: gray">
        <a-icon type="tag"/>
        {{tag.name}}
      </a>
      <a key="eye" style="color: gray">
        <a-icon type="eye"/>
        {{blog.viewCount}}</a>
    </div>
    <!--博客内容部分pulse fadeInRight pulse fadeInDown fadeInUp-->
    <div v-html="content" class="animated fadeInUp" style="padding-right: 10px;padding-top: 20px;"></div>

  </div>
</template>

<script>
  import markdown from 'markdown-it'
  import Renderer from 'markdown-it/lib/renderer'
  import hljs from 'highlight.js'
  import { v4 as uuidv4 } from 'uuid'
  import 'highlight.js/styles/ocean.css'
  import '@/assets/css/blog-markdown.preview.css'
  import { mapGetters } from 'vuex'
  import { loadBlogById } from '@/api/blog'

  // 点击某个图片预览案例：https://codesandbox.io/s/v-viewer-zhezr
  import 'viewerjs/dist/viewer.css'
  import Viewer from 'v-viewer/src/component.vue'
  //aos动画
  import AOS from 'aos/dist/aos'
  import 'aos/dist/aos.css'
  //博客系统配置
  import config from '@/config/blogSetting'

  export default {
    name: 'BlogContent',
    components: {
      Viewer
    },
    data() {
      return {
        content: '',
        blogId: null,//特殊页面（除博文意外，比如：关于、友链、其他说明文档）
        viewerOptions: {},
        previewImages: [],//预览图片
        md: null         //markdown对象
      }
    },
    computed: {
      ...mapGetters(['treeOutline', 'listOutline', 'blog'])
    },
    created() {
      //需要加载的目标特殊页面（优先判断）
      // markdown编辑器初始化
      this.initMarkdown()
      // markdown默认渲染方法重写
      this.overrideMarkdownRender()
    },
    mounted() {
      // 博文内容初始化
      this.initBlogContnet()
      // 初始化动画
      AOS.init({
        easing: 'ease-in-out-sine'
      })
    },
    methods: {
      inited(viewer) {
        this.$viewer = viewer
      },
      show(previewIndex) {
        // this.$viewer.show(3)
        this.$viewer.view(previewIndex)
      },
      /**
       * 初始化markdown编辑器对象
       */
      initMarkdown() {
        let utils = require('markdown-it/lib/common/utils')
        this.md = markdown({
          highlight: function(str, lang) {
            if (lang && hljs.getLanguage(lang)) {
              try {
                return '<pre class="hljs"><code>' +
                  hljs.highlight(lang, str, true).value +
                  '</code></pre>'
              } catch (__) {
              }
            }
            return '<pre class="hljs"><code>' + utils.escapeHtml(str) + '</code></pre>'
          }
        })
      },
      /**
       * markdown渲染规则重写，渲染同时生成目录树对象
       * @param md
       */
      overrideMarkdownRender() {
        let utils = require('markdown-it/lib/common/utils')
        let vm = this
        Renderer.prototype.render = (tokens, options, env) => {
          var i, len, type,
            result = '',
            rules = md.renderer.rules
          for (i = 0, len = tokens.length; i < len; i++) {
            type = tokens[i].type
            // 处理标题类型的tokens
            if (type === 'heading_open') {
              let uuid = uuidv4()
              tokens[i].attrs = [['id', uuid]]
              //动画:data-aos="zoom-in"
              let node = {
                id: uuid,
                tag: tokens[i].tag,
                text: tokens[i + 1].content,
                children: []
              }
              // 记录和改造目录数据
              this.listOutline.push(node)
              this.buildOutlineTreeNode(node)
            }
            if (type === 'inline') {
              result += md.renderer.renderInline(tokens[i].children, options, env)
            } else if (typeof rules[type] !== 'undefined') {
              result += rules[tokens[i].type](tokens, i, options, env, this)
            } else {
              result += md.renderer.renderToken(tokens, i, options, env)
            }
          }

          //通知给blog-detail模块
          const treeOutline = this.treeOutline
          const listOutline = this.listOutline
          this.$store.dispatch('initOutlineData', { treeOutline, listOutline })
          return result
        }
        const md = this.md
        //code_block代码块重写
        this.md.renderer.rules.code_block = function(tokens, idx, options, env, slf) {
          var token = tokens[idx]
          return '<pre' + md.renderer.renderAttrs(token) + '><code>' +
            utils.escapeHtml(tokens[idx].content) +
            '</code></pre>\n'
        }
        //图片渲染规则重写
        let iamgeIndex = 0
        this.md.renderer.rules.image = function(tokens, idx, options, env, slf) {
          var token = tokens[idx]
          var imageSrc = token.attrs[token.attrIndex('src')][1]
          //追加图片样式
          token.attrs.push(['style', 'cursor:pointer'])
          token.attrs.push(['data-source', imageSrc])
          token.attrs.push(['index', iamgeIndex])
          token.attrs.push(['class', 'blog-image'])
          iamgeIndex++
          //动画: data-aos="zoom-in-down"
          //token.attrs.push(['data-aos', 'fade-up'])
          vm.previewImages.push(imageSrc)
          token.attrs[token.attrIndex('alt')][1] =
            slf.renderInlineAsText(token.children, options, env)
          return slf.renderToken(tokens, idx, options)
        }
      },
      /**
       * 向上递归查找，直到查找到级别一样的节点或者根节点位置，将同级别节点的parentId或者'root'赋值给当前节点
       * @param cursorNode 当前节点
       * @param insertNode 插入节点
       * @returns {*} 返回父节点
       */
      queryParentNode(cursorNode, insertNode) {
        // 定义一个新的指针
        if (cursorNode.tag === insertNode.tag) {
          // 例如：h4 === h4 ,说明同级
          return cursorNode.parent
        } else if (insertNode.tag > cursorNode.tag) {
          // 例如：h4 > h3，说明是下级
          return cursorNode
        } else {
          // 例如：h2 > h4，说明是上级
          cursorNode = cursorNode.parent
          return this.queryParentNode(cursorNode, insertNode)
        }
      },

      /**
       * 构建树形目录
       * @param insertNode 插入的节点对象
       */
      buildOutlineTreeNode(insertNode) {
        if (!this.cursorNode) this.cursorNode = this.treeOutline
        // 第一个插入节点
        if (this.cursorNode.id === 'root') {
          insertNode.parent = this.treeOutline
          this.cursorNode.children.push(insertNode)
          this.cursorNode = insertNode
          return
        }
        let parentNode
        if (insertNode.tag > this.cursorNode.tag) {
          // 插入节点级别低
          parentNode = this.cursorNode
        } else if (insertNode.tag === this.cursorNode.tag) {
          // 同级别
          parentNode = this.cursorNode.parent
        } else {
          // 插入节点级别高
          parentNode = this.queryParentNode(this.cursorNode, insertNode)
        }
        insertNode.parent = parentNode
        parentNode.children.push(insertNode)
        this.cursorNode = insertNode
      },

      /**
       * 初始化博文内容
       * @param md markdown对象
       */
      initBlogContnet() {
        const vm = this
        let blogId = this.$route.params.blogId
        loadBlogById(blogId).then(res => {
          if (res.code === '200') {
            this.$store.dispatch('setBlogDetail', { blog: res.data })
            let renderMdUrl
            let mdConfigUrl = config.especialPageTyps[this.blogId]
            if (this.blogId && mdConfigUrl) {
              renderMdUrl = mdConfigUrl
            } else {
              renderMdUrl = '/jenkins.md'
            }
            vm.content = vm.md.render(this.blog.content)
            //绑定图片点击事件
            setTimeout(function() {
              let images = document.getElementsByClassName('blog-image')
              for (let img of images) {
                img.onclick = () => {
                  vm.show(img.getAttribute('index'))
                }
              }
            }, 1000)
          }
        }).catch(err => {
        })
      }
    }
  }

</script>

<style scoped>
  .example {
    display: flex;
    height: calc(100vh - 30px);
  }

  .content {
    border: 1px solid #3361d8;
    margin: 0 20px;
    padding: 40px;
    border-radius: 4px;
    flex: auto;
    overflow: auto;
  }

  .navigation {
    width: 16rem;
    flex-shrink: 0;
    padding: .5rem;
    border-radius: 2px;
    text-align: left;
    overflow: auto;
  }

  .title {
    font-size: 1rem;
    border-bottom: 1px solid #c9c9c9;
    margin: .5rem;
  }

  .node-render-content {
    color: #3361D8;
    user-select: none;
    cursor: pointer;
    margin: 2px 0;
  }

  .node-render-content:hover {
    text-decoration: underline;
  }

  .node-render-content:active {
    position: relative;
    left: 1px;
    top: 1px;
  }

  /*博客大纲固定*/
  .outline-fixed {
    position: fixed;
    top: 100px;
    left: 30px;
  }

  .blogOutlineContainer >>> .ant-menu-inline, .ant-menu-vertical, .ant-menu-vertical-left {
    border-right: 1px solid transparent;
  }

  .blog-tag-category {
    text-align: center;
  }

  .blog-tag-category a {
    text-decoration: none;
    margin-left: 10px;
  }

  .blog-tag-category a:hover {
    font-weight: bold;
    /*color: black;*/
  }
  .blog-content{
  }
</style>
